package com.examples.gateway.helper.filter;

import com.examples.core.res.CommonResult;
import com.examples.core.res.IResultCode;
import com.examples.core.tool.constant.ExamplesConstants;
import com.examples.core.tool.utils.json.JsonUtils;
import com.examples.gateway.helper.config.GatewayHelperProperties;
import com.google.common.base.Joiner;
import lombok.*;
import lombok.extern.slf4j.Slf4j;
import org.jetbrains.annotations.NotNull;
import org.reactivestreams.Publisher;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.core.io.buffer.DataBufferFactory;
import org.springframework.core.io.buffer.DataBufferUtils;
import org.springframework.core.io.buffer.DefaultDataBufferFactory;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.http.server.reactive.ServerHttpResponseDecorator;
import org.springframework.stereotype.Component;
import org.springframework.util.AntPathMatcher;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

import java.io.Serializable;
import java.nio.charset.StandardCharsets;
import java.time.LocalDateTime;

import static org.springframework.cloud.gateway.support.ServerWebExchangeUtils.ORIGINAL_RESPONSE_CONTENT_TYPE_ATTR;

/**
 * @ClassName: WrapperResponseGlobalFilter
 * @Description: TODO
 * @Author: DAIYALU
 * @Date: 2024/5/14
 * @Version: 1.0
 */
@Slf4j
@Component
@RequiredArgsConstructor
public class ResponseGlobalFilter extends AbstractGlobalHelperFilter {
    private final static Joiner JOINER = Joiner.on("");
    private final AntPathMatcher matcher = new AntPathMatcher();

    private final GatewayHelperProperties gatewayHelperProperties;

    @Override
    public Mono<Void> doFilter(ServerWebExchange exchange, GatewayFilterChain chain) {
        ServerHttpResponse originalResponse = exchange.getResponse();
        DataBufferFactory dataBufferFactory = new DefaultDataBufferFactory();
        ServerHttpResponseDecorator response = new ServerHttpResponseDecorator(originalResponse) {

            @NotNull
            @Override
            public Mono<Void> writeWith(@NotNull Publisher<? extends DataBuffer> body) {
                HttpStatus status = getStatusCode();
                String path = exchange.getRequest().getURI().getPath();
                GatewayHelperProperties.ReWriteBody reWriteBody = gatewayHelperProperties.getReWriteBody();
                if (null != reWriteBody && reWriteBody.getResponse().stream().anyMatch(t -> matcher.match(t, path))) {
                    return super.writeWith(body);
                }
                if (HttpStatus.OK.equals(status) && body instanceof Flux) {
                    // 获取ContentType，判断是否返回JSON格式数据
                    String respContentType = exchange.getAttribute(ORIGINAL_RESPONSE_CONTENT_TYPE_ATTR);
                    if (null != respContentType && respContentType.contains(ExamplesConstants.RESPONSE_CONTENT_TYPE)) {
                        Flux<? extends DataBuffer> fluxBody = Flux.from(body);
                        //（返回数据内如果字符串过大，默认会切割）解决返回体分段传输
                        Flux<DataBuffer> result = fluxBody.buffer().map(dataBuffers -> {
                            DataBuffer join = dataBufferFactory.join(dataBuffers);
                            byte[] content = new byte[join.readableByteCount()];
                            join.read(content);
                            DataBufferUtils.release(join);
                            String bodyStr = new String(content, StandardCharsets.UTF_8);
                            // 转成字节
                            byte[] bytes = bodyStr.getBytes();
                            log.info("【网关】返回值：{}", new String(bytes, StandardCharsets.UTF_8));
                            originalResponse.getHeaders().setContentLength(bytes.length);
                            return dataBufferFactory.wrap(bytes);
                        });
                        return super.writeWith(result);
                    }
                }else if(HttpStatus.INTERNAL_SERVER_ERROR.equals(status)){
                    log.error("请求异常，异常状态码：{}",status);
                    CommonResult<ServerErrorInfo> info = new CommonResult<ServerErrorInfo>();
                    info.setCode(IResultCode.FAILURE);
                    info.setMsg("未知异常");
                    info.setData(ServerErrorInfo.builder()
                            .timestamp(LocalDateTime.now().toString())
                            .status(HttpStatus.INTERNAL_SERVER_ERROR.value())
                            .error(HttpStatus.INTERNAL_SERVER_ERROR.getReasonPhrase())
                            .path(exchange.getRequest().getPath().value())
                            .build());
                    DataBuffer dataBuffer = dataBufferFactory.wrap(JsonUtils.toJsonString(info).getBytes(StandardCharsets.UTF_8));
                    setStatusCode(HttpStatus.OK);
                    return super.writeAndFlushWith(Flux.just(dataBuffer).map(Flux::just));
                }
                return super.writeWith(body);
            }

            @NotNull
            @Override
            public Mono<Void> writeAndFlushWith(@NotNull Publisher<? extends Publisher<? extends DataBuffer>> body) {
                return writeWith(Flux.from(body).flatMapSequential(p -> p));
            }
        };
        return chain.filter(exchange.mutate().response(response).build());
    }

    @Data
    @Builder
    @AllArgsConstructor
    @NoArgsConstructor
    public static class ServerErrorInfo implements Serializable {

        private static final long serialVersionUID = -6604756894349341589L;

        private String timestamp;

        private Integer status;

        private String error;

        private String path;
    }


    @Override
    public int getOrder() {
        return -2;
    }
}
